package gameoflife;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Rule {

	private final String name;
	private final String ruleString;
	private final int survivalMask, birthMask;

	public static final Rule LIFE = createFromString("Life", "23/3");
	public static final List<Rule> RULES = new ArrayList<Rule>();
	static {
		InputStream input = Rule.class.getResourceAsStream("rules");
		BufferedReader reader = new BufferedReader(new InputStreamReader(input));
		try {
			while (reader != null && reader.ready()) {
				String[] columns = reader.readLine().split("\\s", 2);
				if (columns.length == 2) {
					Rule rule = Rule.createFromString(columns[1], columns[0]);
					RULES.add(rule);
				}
			}
		} catch (IOException e) {
		}
	}

	public boolean survives(int neighbors) {
		return (survivalMask & (1 << neighbors)) != 0;
	}

	public boolean born(int neighbors) {
		return (birthMask & (1 << neighbors)) != 0;
	}

	private Rule(String name, Collection<Integer> survivalSet, Collection<Integer> birthSet) {
		this.name = name;
		int survivalMask = 0;
		int birthMask = 0;
		for (int i : survivalSet) {
			survivalMask |= 1 << i;
		}
		for (int i : birthSet) {
			birthMask |= 1 << i;
		}
		this.survivalMask = survivalMask;
		this.birthMask = birthMask;

		StringBuilder builder = new StringBuilder("S");
		for (Integer i : survivalSet) {
			builder.append(i);
		}
		builder.append("/B");
		for (Integer i : birthSet) {
			builder.append(i);
		}
		ruleString = builder.toString();
	}

	public String getName() {
		return name;
	}

	public String getRuleString() {
		return ruleString;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		return o instanceof Rule && survivalMask == ((Rule)o).survivalMask && birthMask == ((Rule)o).birthMask;
	}

	@Override
	public int hashCode() {
		return survivalMask ^ birthMask << 8;
	}

	@Override
	public String toString() {
		if (name == null || name.isEmpty()) {
			return ruleString;
		}
		return name + " " + ruleString;
	}

	public static Rule createFromString(String ruleString) {
		return createFromString(null, ruleString);
	}

	public static Rule createFromString(String name, String ruleString) {
		if (ruleString != null) {
			String[] digits = ruleString.split("/", 2);
			if (digits.length == 2) {
				String survival = digits[0];
				String birth = digits[1];
				if (digits[0].toLowerCase().startsWith("b")) {
					// swap
					survival = digits[1];
					birth = digits[0];					
				}
				Rule rule = new Rule(name, parseDigits(survival, "012345678"), parseDigits(birth, "12345678"));
				if (RULES != null) {
					int index = RULES.indexOf(rule);
					if (index > -1) {
						rule = RULES.get(index);
					}
				}
				return rule;
			}
		}
		return null;
	}

	private static Set<Integer> parseDigits(String digits, String allowedDigits) {
		Set<Integer> set = new HashSet<Integer>();
		if (digits != null) {
			for (int i = 0; i < digits.length(); i++) {
				try {
					String digit = digits.substring(i, i + 1);
					if (allowedDigits.contains(digit)) {
						set.add(new Integer(digit));
					}
				} catch (NumberFormatException ex) {
					continue;
				}
			}
		}
		return set;
	}

}
